/*
 * Copyright (c) Huawei Device Co., Ltd. 2023. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "apdu_validator.h"
#include <sstream>
#include "common_utils.h"
#include "loghelper.h"
#include "se_common_exception.h"

namespace OHOS {
namespace SE {
void ApduValidator::CheckCommand(const std::string& apdu)
{
    // correct format: [command header 4B] + [Lc field 0/1/3B] + [cmd data filed Nc Bytes] + [Le field 0~3B]
    InfoLog("Transmit apdu is: %{public}s", apdu.c_str());
    if (CommonUtils::GetHexStringLength(apdu) < CMD_APDU_LENGTH_CASE1) {
        std::stringstream errorMsg;
        errorMsg << "Invalid length for command (" << CommonUtils::GetHexStringLength(apdu) << ").";
        throw IllegalArgumentError(errorMsg.str());
    }
    CheckCla(CommonUtils::GetHexStrByte(apdu, CMD_HEAD_CLA));
    CheckIns(CommonUtils::GetHexStrByte(apdu, CMD_HEAD_INS));

    if (CommonUtils::GetHexStringLength(apdu) == CMD_APDU_LENGTH_CASE1) {
        return;  // Case 1: [command header 4B]
    }

    if (CommonUtils::GetHexStringLength(apdu) == CMD_APDU_LENGTH_CASE2) {
        CheckLe(CommonUtils::GetHexStrByte(apdu, CMD_NO_LC_LE_INDEX));
        return;  // Case 2: [command header 4B] + [Le field 1B]
    }

    int lc = CommonUtils::GetHexStrByte(apdu, CMD_LC_INDEX);
    if (lc != 0x00) {
        if (CommonUtils::GetHexStringLength(apdu) == CMD_APDU_LENGTH_CASE3_WITHOUT_DATA + lc) {
            return;  // Case 3:  [command header 4B] + [Lc field 1B] + [cmd data filed Nc Bytes]
        }
        if (CommonUtils::GetHexStringLength(apdu) == CMD_APDU_LENGTH_CASE4_WITHOUT_DATA + lc) {
            CheckLe(CommonUtils::GetHexStrByte(apdu, (CommonUtils::GetHexStringLength(apdu) - 1)));
            return;  // Case 4: command header 4B] + [Lc field 1B] + [cmd data filed Nc Bytes] + [Le field 1B]
        }
        std::stringstream errorMsg;
        errorMsg << "Unexpected value of Lc (" << lc << ")";
        throw IllegalArgumentError(errorMsg.str());
    }
    std::stringstream errorMsg;
    errorMsg << "Unexpected value of Lc (" << lc << ")";
    throw IllegalArgumentError(errorMsg.str());
}

void ApduValidator::CheckCla(size_t cla)
{
    if (cla == 0xFF) {
        std::stringstream errorMsg;
        errorMsg << "Invalid value of CLA (" << std::hex << cla << ")";
        throw IllegalArgumentError(errorMsg.str());
    }
}

void ApduValidator::CheckIns(size_t ins)
{
    if ((ins & 0xF0) == 0x60 || ((ins & 0xF0) == 0x90)) {
        std::stringstream errorMsg;
        errorMsg << "Invalid value of INS (" << std::hex << ins << "). 0x6X and 0x9X are not valid values";
        throw IllegalArgumentError(errorMsg.str());
    }
}

void ApduValidator::CheckLe(uint32_t le)
{
    if (le > MAX_EXPECTED_DATA_LENGTH) {
        std::stringstream errorMsg;
        errorMsg << "Invalid value for le parameter (" << le << ").";
        throw IllegalArgumentError(errorMsg.str());
    }
}
}  // namespace SE
}  // namespace OHOS